package node;
import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Iterator;

import log.Logger;
import messaging.AddNodeMessage;
import messaging.InterNodeMessage;
import messaging.MessageHashing;
import remote.RemoteDistributedHashMap;
/**
 * This class is the main class that does all the processing for Node Addition.
 * It is launched from the LaunchAddNode.
 * @author Team 4
 * @version Spring 2013
 */
public class NodeAddition
{
  /**
   * the node number to be added.
   */
  private int my_node_num;
  /**
   * the key for add node message.
   */
  private int ADD_NODE = 6;
  /**
   * A reference to nodemanager.
   */
  private NodeManager nodeman;
  /**
   * The constructor for the Adding Node class.It stores the node number for which 
   * this class is used to add a node.
   * @param the_node_num
   */
  public NodeAddition(final int the_node_num,NodeManager nodeman) 
  {
    this.my_node_num = the_node_num;
    this.nodeman = nodeman;
  }
  /**
   * This method returns an array list of nodes that this new actual node need to 
   * create and manage.
   * @param node_num - the Node number for which the method 
   * calculates the nodes it will manage.
   * @return Array list of nodes.
   */
  private ArrayList<Integer> nodes_toAdd(int node_num) 
  {
    final ArrayList<Integer> list_to_return = new ArrayList<Integer>();
    list_to_return.add(my_node_num);
    int index = node_num + 1;
    if (index == NodeManager.total_nodes_chord)
    {
      index = 0;
    }
    while (nodeman.rmi_NodeFind(index).endsWith("V"))
    {
      list_to_return.add(index);
      index++;
      if (index == NodeManager.total_nodes_chord)
      {
        index = 0;
      }
    }
    return list_to_return;
  }
  /**
   * find the predecessor actual node  of this node.
   * @param node_num - the node number for which the predecessor actual node is found.
   * @return int - the predecessor actual node for this node number.
   */
  public int findPredecessor(final int node_num) 
  {
    int index = node_num - 1;
    if (index == NodeManager.total_nodes_chord)
    {
      index = 0;
    }
    if (index < 0)
    {
      index = NodeManager.total_nodes_chord - 1;
    }
    while (nodeman.rmi_NodeFind(index).endsWith("V"))
    {
      index--;
      if (index == NodeManager.total_nodes_chord)
      {
        index = 0;
      }
      if (index < 0)
      {
        index = NodeManager.total_nodes_chord - 1;
      }
    }
    Logger.out(" the predecessor is node " + index);
    Logger.writeContent(nodeman.LOGGER_FILE_NAME, " the predecessor is node " + index);
    return index;
  }

  /**
   * Add the new actual Node to the registry and send a message to its predecessor 
   * about this new node.
   */
  public void addActualNode() 
  {
    try
    {
      ArrayList<Integer> nodes_to_add = new ArrayList<Integer>();
      nodes_to_add = this.nodes_toAdd(this.my_node_num);
      Logger.out(" the nodes that the new node " + this.my_node_num + " manages are - ");
      Logger.writeContent(nodeman.LOGGER_FILE_NAME, " the nodes that the new node " +
         this.my_node_num + " manages are - ");
      final Iterator<Integer> itr = nodes_to_add.iterator();
      while (itr.hasNext())
      {
        final int node_num = itr.next();
        Logger.out(" node " + node_num);
        Logger.writeContent(nodeman.LOGGER_FILE_NAME, " node " + node_num);
      }
      final AddNodeMessage message = new AddNodeMessage(nodes_to_add, null);
      final int the_predecessor = this.findPredecessor(this.my_node_num);
      final int dest_node = nodeman.hashhelp.optimalNodeFind(this.my_node_num, the_predecessor);
     
      final InterNodeMessage fin_mess = new InterNodeMessage(this.my_node_num, 
                                                             the_predecessor, 
                                                             this.my_node_num, 
                                                             dest_node, ADD_NODE);
      fin_mess.setAnswer_set(false);
      fin_mess.set_AddNode_Message(message);
      final String dest_node_str = nodeman.rmi_NodeFind(dest_node);
      final RemoteDistributedHashMap to_send =
        (RemoteDistributedHashMap) NodeManager.registry.lookup(dest_node_str);
      Logger.writeContent(nodeman.LOGGER_FILE_NAME, 
                          " sending a message to predecessor node " +
                          the_predecessor + " for adding node");
      Logger.out(" sending a message to predecessor node " + 
          the_predecessor + " for adding node");
      Logger.out(" message routed through node " + dest_node);
      Logger.writeContent(nodeman.LOGGER_FILE_NAME,
                          " message routed through node " + dest_node);
      to_send.send(fin_mess);
    }    
    catch (final InterruptedException e)
    {
      Logger.writeContent(nodeman.LOGGER_FILE_NAME, 
                          "interrupted when sending message:" + e);
      System.exit(1);
    }
    catch (final AccessException e)
    {
      Logger.writeContent(nodeman.LOGGER_FILE_NAME, "issue accessing registry:" + e);
      System.exit(1);
    }
    catch (final NotBoundException e)
    {
      Logger.writeContent(nodeman.LOGGER_FILE_NAME, "issue binding to  registry:" + e);
      System.exit(1);
    }
    catch (final RemoteException e)
    {
      Logger.writeContent(nodeman.LOGGER_FILE_NAME, "issue accessing registry:" + e);
      System.exit(1);
    }
  }
}

